﻿#include <cstdlib>
#if defined(USE_EXPERIMENTAL_FS)
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
#else
#include <filesystem>
namespace fs = std::filesystem;
#if defined(__APPLE__)
#include <unistd.h>
#endif
#endif
#include <signal.h>
#include <cstdint>
#include <iomanip>

#include <pthread.h>
#include <json/json.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <ctime>
#include <sys/epoll.h>
#include <fcntl.h>
#include "fifo.h"
#include "serial.h"
#include "fx3_rcp_command.h"
#include "CRSDK/CameraRemote_SDK.h"
#include "CameraDevice.h"
#include "Text.h"
// #define no_camera
// 此处名字需与'sony环境配置.txt'->USB配置中绑定的名字一样
const char *dev = "/dev/mcu_serial";
namespace SDK = SCRSDK;
using namespace Json;

// serial_t cur, last;
// tomcu_t tomcu;
filename_t file;
// serialPort myserial;
int epoll_fd = -1;
int read_rcp_fifo = -1;
int write_rcp_fifo = -1;
volatile sig_atomic_t keep_running = 1;

// pthread_t serial_thread_tid, usbman_thread_tid, key_input_tid;
// char action;
// void *serial_thread(void *data);
// void *usbman_thread(void *arg);
// void *key_input_thread(void *arg);
// void send_json();
void record_time(char fun, char res); // 1:end 2:start
// void start_time(void);                // record program start time
int epoll_add(int fd, int epoll_fd, uint32_t events)
{
    struct epoll_event event;
    // 5. 将串口 fd 添加到 epoll 监控 (监听可读事件)
    event.events = events; // 监听可读
    event.data.fd = fd;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event) == -1)
    {
        perror("epoll_ctl: serial_fd");
        close(fd);
        return -1;
    }
    return 0;
}
int handle_rcp_msg(uint8_t *data, uint8_t nread, std::shared_ptr<cli::CameraDevice> camera)
{
    uint8_t res = -1;
    request_command_t *rc = (request_command_t *)data;
    cli::tout << "request command:" << rc->cmd << "\r\n";
    switch (rc->cmd)
    {
    case CAPTURE_IMAGE:
        camera->capture_image();
        res = 1;
        break;
    case S1_SHOOTING:
        camera->s1_shooting();
        res = 1;
        break;
    case AF_SHUTTER:
        camera->af_shutter();
        res = 1;
        break;
    case SET_IOS:
        if (rc->rw)
            res = camera->set_iso(rc->param1);
        else
            res = camera->get_iso();
        break;
    case SET_SHUTTER_SPEED:
        if (rc->rw)
            res = camera->set_shutter_speed(rc->param1);
        else
            res = camera->get_shutter_speed();
        break;
    case SET_EXPOSURE_PROGRAM_MODE:
        if (rc->rw)
            res = camera->set_exposure_program_mode(rc->param1);
        else
            res = camera->get_exposure_program_mode();
        break;
    case SET_FOCUS_MODE:
        if (rc->rw)
            res = camera->set_focus_mode(rc->param1);
        else
            res = camera->get_focus_mode();
        break;
    case SET_FOCUS_AREA:
        if (rc->rw)
            res = camera->set_focus_area(rc->param1);
        else
            res = camera->get_focus_area();
        break;
    case DEVICE_PROPERTY_FEL:
        res = camera->execute_lock_property((CrInt16u)SDK::CrDevicePropertyCode::CrDeviceProperty_FEL, rc->param1);
        break;
    case DEVICE_PROPERTY_AWBL:
        res = camera->execute_lock_property((CrInt16u)SDK::CrDevicePropertyCode::CrDeviceProperty_AWBL, rc->param1);
        break;
    case SET_AF_AREA_POSITION:
        res = camera->set_af_area_position(rc->param1, rc->param2);
        break;
    case SET_SELECT_MEDIA_FORMAT:
        res = camera->set_select_media_format(rc->param1, rc->param2);
        break;
    case EXECUTE_MOVIE_REC:
        res = camera->execute_movie_rec(rc->param1);
        // record_time(rc->param1, res);
        break;
    case SET_WHITE_BALANCE:
        if (rc->rw)
            res = camera->set_white_balance(rc->param1);
        else
            res = camera->get_white_balance();
        break;
    case SET_CUSTOM_WB:
        res = camera->set_custom_wb(rc->param1, rc->param2);
        break;
    case SET_ZOOM_OPERATION:
        res = camera->set_zoom_operation(rc->param1);
        break;
    case SET_REMOCON_ZOOM_SPEED_TYPE:
        if (rc->rw)
            res = camera->set_remocon_zoom_speed_type(rc->param1);
        else
            res = camera->get_remocon_zoom_speed_type();
        break;
    case SET_RECORDING_SETTING:
        res = camera->set_recording_setting(rc->param1);
        break;
    case SET_DISPMODE:
        res = camera->set_dispmode();
        break;
    case SET_PLAYBACK_MEDIA:
        res = camera->set_playback_media(rc->param1);
        break;
    default:
        break;
    }
}
int recv_rcpmsg(int fd, std::shared_ptr<cli::CameraDevice> camera)
{
    uint8_t data[64];
    uint8_t nread;

    nread = read(fd, data, sizeof(data));
    if (nread < 0)
    {
        return -1;
    }
    else
    {
        // output_data("read rcp data:", data, nread);

        handle_rcp_msg(data, nread, camera);
    }
    // send_json();
}
int main()
{
    std::locale::global(std::locale(""));
    cli::tin.imbue(std::locale());
    cli::tout.imbue(std::locale());
    char key_input;
    CrInt32u version = SDK::GetSDKVersion();
    cli::tout << "RemoteSampleApp v1.09.00 running...\n";

    memset(&file, 0, sizeof(filename_t));
    // 初始化相机
    auto init_success = SDK::Init();
    if (!init_success)
    {
        cli::tout << "Failed to initialize Remote SDK. Terminating.\n";
        SDK::Release();
        std::exit(EXIT_FAILURE);
    }
    cli::tout << "Remote SDK successfully initialized.\n";
    SDK::ICrEnumCameraObjectInfo *camera_list = nullptr;
    auto enum_status = SDK::EnumCameraObjects(&camera_list);
    if (CR_FAILED(enum_status) || camera_list == nullptr)
    {
        cli::tout << "No cameras detected. Connect a camera and retry.\n";
        SDK::Release();
        std::exit(EXIT_FAILURE);
    }

    auto ncams = camera_list->GetCount();
    cli::tout << "Camera enumeration successful. num:" << ncams << " \n";

    for (CrInt32u i = 0; i < ncams; ++i)
    {
        auto camera_info = camera_list->GetCameraObjectInfo(0);
        cli::text conn_type(camera_info->GetConnectionTypeName());
        cli::text model(camera_info->GetModel());
        cli::text id = TEXT("");
        if (TEXT("IP") == conn_type)
        {
            cli::NetworkInfo ni = cli::parse_ip_info(camera_info->GetId(), camera_info->GetIdSize());
            id = ni.mac_address;
        }
        else
        {
            id = ((TCHAR *)camera_info->GetId());
        }
        // cli::tout << '[' << 0 + 1 << "] " << model.data() << " (" << id.data() << ")\n";
    }

    typedef std::shared_ptr<cli::CameraDevice> CameraDevicePtr;
    typedef std::vector<CameraDevicePtr> CameraDeviceList;
    CameraDeviceList cameraList; // all
    std::int32_t cameraNumUniq = 1;
    std::int32_t selectCamera = 1;

    auto *camera_info = camera_list->GetCameraObjectInfo(0);

    cli::tout << "Create camera SDK camera callback object.\n\n";
    CameraDevicePtr camera = CameraDevicePtr(new cli::CameraDevice(cameraNumUniq, camera_info));
    cameraList.push_back(camera); // add 1st

    camera_list->Release();

    if (camera->is_connected())
    {
        cli::tout << "Please disconnect\n";
    }
    else
    {
        camera->connect(SDK::CrSdkControlMode_Remote, SDK::CrReconnecting_ON);
    }
    // 分割线内为官方源码简化版，源码适用多个相机对象，此项目只用一个，因此简化
    ////////////////////////////////////////////////////////////////f
    // key_input = action;
    int ret, nfds;
    struct epoll_event events[MAX_EVENTS];

    epoll_fd = epoll_create1(0);
    if (epoll_fd == -1)
    {
        cli::tout << "epoll_create1\n";
        goto end;
    }
    write_rcp_fifo = open_fifo(CAM2RCP_FIFO);
    if (write_rcp_fifo < 0)
    {
        cli::tout << "open filename :" << CAM2RCP_FIFO;
        goto end;
    }
    read_rcp_fifo = open_fifo(RCP2CAM_FIFO);
    if (read_rcp_fifo < 0)
    {
        cli::tout << "open filename :" << RCP2CAM_FIFO;
        goto end;
    }
    ret = epoll_add(read_rcp_fifo, epoll_fd, EPOLLIN);
    if (ret < 0)
    {
        cli::tout << "epoll_add\n";
        goto end;
    }
    while (1)
    {
        // 等待事件发生，-1 表示无限等待
        nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, 500);
        if (nfds == -1)
        {
            if (errno == EINTR)
            {
                // 被信号中断，检查 keep_running 并继续或退出
                if (!keep_running)
                    break;
                continue;
            }
            else
            {
                cli::tout << "epoll_wait\n";
                break;
            }
        }
        else if (nfds == 0)
        {
            cli::tout << "=>timout\n";
        }
        for (int i = 0; i < nfds; ++i)
        {
            if (events[i].data.fd == read_rcp_fifo)
            {
                recv_rcpmsg(read_rcp_fifo, camera);
            }
        }
    }
    SDK::Release();
end:
    if (epoll_fd > 0)
        close(epoll_fd);
    if (write_rcp_fifo > 0)
        close(write_rcp_fifo);
    if (read_rcp_fifo > 0)
        close(read_rcp_fifo);
    std::exit(EXIT_FAILURE);
}
#if 0
void *usbman_thread(void *arg)
{
// ch340热插拔线程
#define UEVENT_BUFFER_SIZE 2048
    struct sockaddr_nl client;
    struct timeval tv;
    int CppLive, rcvlen, ret;
    fd_set fds;
    int buffersize = 1024;
    CppLive = socket(AF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
    memset(&client, 0, sizeof(client));
    client.nl_family = AF_NETLINK;
    client.nl_pid = getpid();
    client.nl_groups = 1; /* receive broadcast message*/
    setsockopt(CppLive, SOL_SOCKET, SO_RCVBUF, &buffersize, sizeof(buffersize));
    bind(CppLive, (struct sockaddr *)&client, sizeof(client));
    while (1)
    {
        char buf[UEVENT_BUFFER_SIZE] = {0};
        FD_ZERO(&fds);
        FD_SET(CppLive, &fds);
        tv.tv_sec = 0;
        tv.tv_usec = 100 * 1000;
        ret = select(CppLive + 1, &fds, NULL, NULL, &tv);
        if (ret < 0)
            continue;
        if (!(ret > 0 && FD_ISSET(CppLive, &fds)))
            continue;
        /* receive data */
        rcvlen = recv(CppLive, &buf, sizeof(buf), 0);
        if (rcvlen > 0)
        {
            // printf("%s\n", buf);
            if ((strstr(buf, "add") != NULL) && (strstr(buf, "ttyUSB") != NULL) && (serial_thread_tid == 0))
            {
                printf("serial start!\n");
                pthread_create(&serial_thread_tid, NULL, serial_thread, NULL);
                sleep(1);
            }
            if ((strstr(buf, "remove") != NULL) && (strstr(buf, "ttyUSB") != NULL))
            {
                printf("serial stop!\n");
                pthread_cancel(serial_thread_tid);
                serial_thread_tid = 0;
            }
        }
    }
    close(CppLive);
}

void *serial_thread(void *data)
{

    bool inbuffer = false;
    int i, nread, inlast = 0, cmd;
    char buff[buffer_length], read_buffer[buffer_length];
    Reader reader;
    Value root;
    string str;
    bool serial_station;
    serial_station = myserial.OpenPort(dev);
    while (1)
    {
        if (serial_station == false) // 有时打开串口会失败，此处循环确保一定打开
        {
            sleep(1);
            serial_station = myserial.OpenPort(dev);
        }
        else
            break;
    }
    myserial.setup(115200, 0, 8, 1, 'N');

    cli::tout << "serial_thread_running\n";

    while (1)
    {
        nread = myserial.readBuffer(read_buffer, 100);
        // nread代表读到的字节数，空为-1，且一帧命令需要拼接才能复原
        if (nread != -1)
        {
            for (i = 0; i < nread; i++)
            {
                buff[inlast] = read_buffer[i];
                inlast++;
                if (read_buffer[i] == 0x7d)
                    inbuffer = true;
            }
        }

        if (inbuffer)
        {
            str = buff;
            reader.parse(str, root, false);
            last.cmd = root["cmd"].asInt();
            last.fun = root["fun"].asInt();
            string p = root["parameter1"].asString();
            strcpy(last.parm1, p.c_str());
            p = root["parameter2"].asString();
            strcpy(last.parm2, p.c_str());
            memset(buff, 0, 100);
            inbuffer = false;
            inlast = 0;
        }
    }
}

void *key_input_thread(void *arg)
{ // 控制台输入，阻塞式
    while (1)
    {
        cli::tout << "pc input \n";
        cli::tin >> action;
    }
}

void send_json()
{
    char buff[256];
    Value root_send;

    memset(buff, 0, sizeof(buff));
    if (tomcu.cmd == 0)
        return;
    root_send["cmd"] = tomcu.cmd;
    if (tomcu.cmd == 20)
    {
        myserial.read_record(tomcu.res, file.recorstime, file.recoretime);

        sprintf(buff, "%d", tomcu.res);
        root_send["num"] = buff;
        memset(buff, 0, sizeof(buff));
        for (int i = 0; i < tomcu.res; i++)
        {
            sprintf(buff, "%d", i + 1);
            root_send["numserial"] = buff;
            memset(buff, 0, sizeof(buff));
            strcpy(buff, file.filename[i]);
            strcat(buff, "S:");
            strcat(buff, file.creattime[i]);
            for (int j = 0; j < tomcu.res; j++)
            {
                if (strncmp(file.recorstime[j], file.creattime[i], 13) == 0)
                {
                    strcat(buff, ":E:");
                    strcat(buff, file.recoretime[i]);
                    root_send["readname"] = buff;
                    break;
                }
            }
            if (strstr(buff, ":E:") == 0)
            {
                root_send["readname"] = buff;
            }

            string sen = root_send.toStyledString();
            strcpy(buff, sen.c_str());
            myserial.writeBuffer(buff, strlen(sen.c_str()));
        }
        memset(&last, 0, sizeof(serial_t));
        return;
    }
    sprintf(buff, "%d", tomcu.res);
    root_send["result"] = buff;
    memset(buff, 0, sizeof(buff));
    string str = root_send.toStyledString();
    strcpy(buff, str.c_str());
    printf("%s\n", buff);
    myserial.writeBuffer(buff, strlen(str.c_str()));
    memset(&last, 0, sizeof(serial_t));
}

void record_time(char fun, char res) // 1:end 2:start
{
    time_t nowtime;
    FILE *fp;
    char date[100];

    if (res == -1)
        return;
    // 读取系统时间，并写入record_date.txt中，因为没有rtc功能，因此掉电会丢时钟
    time(&nowtime);
    tm *p = localtime(&nowtime);
    fp = fopen("/homg/pi/record_date.txt", "a");
    memset(date, 0, sizeof(date));
    if (fun == '2')
    {
        fprintf(fp, "%04d%02d%02dT%02d%02d%02d:", p->tm_year + 1900, p->tm_mon + 1, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);
    }
    else if (fun == '1')
    {
        fprintf(fp, "%04d%02d%02dT%02d%02d%02d\n", p->tm_year + 1900, p->tm_mon + 1, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);
    }
    fclose(fp);
    printf("write finish\n");
}
void start_time(void)
{
    time_t nowtime;
    // FILE *fp;
    char date[128] = {0};
    time(&nowtime);
    tm *p = localtime(&nowtime);
    memset(date, 0, sizeof(date));
    sprintf(date, "%04d-%02d-%02d-%02d:%02d:%02d", p->tm_year + 1900, p->tm_mon + 1, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);
    printf("Program Start Time:%s\n", date);
}
#endif